﻿using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Linq;
using PropertyExpression.Common;

namespace PropertyExpression.ControllableQuery.Test
{
    public static class EnumerableEx
    {
        public static IEnumerable<Option<TSource>> NothingIfEmpty<TSource>(this IEnumerable<TSource> source)
        {
            return source.Any() ? source.Select(_ => _.AsOption()) : new[] {new Option<TSource>()};
        }

        public static IEnumerable<TResult> LeftJoin<TOuter, TInner, TKey, TResult>(this IEnumerable<TOuter> outer,
            IEnumerable<TInner> inner, Func<TOuter, TKey> outerKeySelector, Func<TInner, TKey> innerKeySelector,
            Func<TOuter, Option<TInner>, TResult> resultSelector)
        {
            return outer.GroupJoin(inner, outerKeySelector, innerKeySelector, (outer1, inners) => new {outer1, inners})
                .SelectMany(arg => arg.inners.NothingIfEmpty(), (arg, inner1) => resultSelector(arg.outer1, inner1));
        }
    }

    internal class Program
    {
        private static void Main()
        {
            {
                var stopwatch = new Stopwatch();
                stopwatch.Start();
                new QueryTest().CheckAllQueries();
                stopwatch.Stop();
                Console.WriteLine(stopwatch.ElapsedMilliseconds);
            }

            for (var i = 0; i < 2; i++)
                new QueryTest().CheckAllQueries();

            {
                var stopwatch = new Stopwatch();
                stopwatch.Start();
                const int count = 30;
                for (var i = 0; i < count; i++)
                    new QueryTest().CheckAllQueries();
                stopwatch.Stop();
                Console.WriteLine(stopwatch.ElapsedMilliseconds*1.0/count);
            }
        }

        class Person
        {
            public string FirstName { get; set; }
            public string LastName { get; set; }
        }

        class Pet
        {
            public string Name { get; set; }
            public Person Owner { get; set; }
        }

        public static void LeftOuterJoinExample()
        {
            Person magnus = new Person { FirstName = "Magnus", LastName = "Hedlund" };
            Person terry = new Person { FirstName = "Terry", LastName = "Adams" };
            Person charlotte = new Person { FirstName = "Charlotte", LastName = "Weiss" };
            Person arlene = new Person { FirstName = "Arlene", LastName = "Huff" };

            Pet barley = new Pet { Name = "Barley", Owner = terry };
            Pet boots = new Pet { Name = "Boots", Owner = terry };
            Pet whiskers = new Pet { Name = "Whiskers", Owner = charlotte };
            Pet bluemoon = new Pet { Name = "Blue Moon", Owner = terry };
            Pet daisy = new Pet { Name = "Daisy", Owner = magnus };

            // Create two lists.
            List<Person> people = new List<Person> { magnus, terry, charlotte, arlene };
            List<Pet> pets = new List<Pet> { barley, boots, whiskers, bluemoon, daisy };

            var query = people.LeftJoin(pets, person => person, pet => pet.Owner,
                (person, pet) => new {person.FirstName, PetName = pet.Select(_ => _.Name)});

            foreach (var v in query)
            {
                Console.WriteLine("{0,-15}{1}", v.FirstName + ":", v.PetName);
            }
        }
    }

    internal class Program1
    {
        //TODO: реализовать
        //public static IEnumerable<int> CuillenMonths(string companyName)
        //{
        //    var customerID = new ScalarQuery<int>(
        //        "SELECT CustomerID FROM Customer WHERE CompanyName = @companyName",
        //        new {companyName}).GetResult();
        //    return new Query<T001>(
        //        "SELECT OrderID, OrderDate FROM Order WHERE CustomerID = @customerID",
        //        new {customerID}).GetResult()
        //        .Where(IsCuillen)
        //        .Select(order => order.OrderDate.Month)
        //        .Distinct();
        //}

        //private static bool IsCuillen(T001 order)
        //{
        //    return new Query<T002>(
        //        "SELECT ProductID, Quantity FROM OrderLine WHERE OrderID = @OrderID",
        //        new {order.OrderID}).GetResult()
        //               .Where(line => line.ProductID == 2702)
        //               .Sum(line => line.Quantity) > 14;
        //}
    }

    internal class Program2
    {
        //TODO: реализовать
        //public static IEnumerable<int> CuillenMonths3(string companyName)
        //{
        //    var customerID = new ScalarQuery<int>(
        //        "SELECT CustomerID FROM Customer WHERE CompanyName = @companyName",
        //        new {companyName}).GetResult();
        //    return new Query<T003>(
        //        "SELECT OrderLine.OrderID, OrderDate, ProductID, Quantity " +
        //        "FROM OrderLine JOIN Order ON OrderLine.OrderID = Order.OrderID " +
        //        "WHERE CustomerID = @customerID", new {customerID}).GetResult()
        //        .GroupBy(_ => _.OrderID)
        //        .Select(group => new {
        //            group.FirstOfGroup().OrderDate,
        //            Lines = group
        //        })
        //        .Where(order => order.Lines.Where(line => line.ProductID == 2702)
        //                            .Sum(line => line.Quantity) > 14)
        //        .Select(order => order.OrderDate.Month)
        //        .Distinct();
        //}
    }

    internal class Program3
    {
        //TODO: реализовать
//        public static IEnumerable<int> CuillenMonths(string companyName)
//        {
//            var customerID = new ScalarQuery<int>(
//                "SELECT CustomerID FROM Customer WHERE CompanyName = @companyName",
//                new {companyName}).GetResult();
//            var result = new Query<T001, T004>(@"
//@helper Order() {
//    <text>
//SELECT OrderID, OrderDate FROM Order WHERE CustomerID = @customerID
//    </text>
//}
//@Order()
//SELECT OrderLine.OrderID, ProductID, Quantity 
//FROM OrderLine JOIN (@Order()) Order ON OrderLine.OrderID = Order.OrderID 
//WHERE OrderID = @OrderID", new {customerID}).GetResult();
//            var lines = result.Item2.ToLookup(line => line.OrderID);
//            return result.Item1
//                .Where(order => IsCuillen(lines[order.OrderID]))
//                .Select(order => order.OrderDate.Month)
//                .Distinct();
//        }

//        private static bool IsCuillen(IEnumerable<T004> lines)
//        {
//            return lines.Where(line => line.ProductID == 2702)
//                       .Sum(line => line.Quantity) > 14;
//        }
    }
}